﻿using System;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Web;
using System.Web.Util;
using AzManADAMStore;

namespace AzManADAMStoreRoleProvider
{
    /// <summary>
    /// Proveedor de Autorización basado en AzMAn sobre ujn repositorio ADAM 
    /// </summary>
    public class AzManADAMStoreRoleProvider : System.Web.Security.AuthorizationStoreRoleProvider
    {
        private AzManMgr _AzMan;
        private Dictionary<string, string[]> _CacheRoles;

        // Fields

        /// <summary>
        /// Usuario para logarse contra ADAM
        /// </summary>
        private string _UserNameToLogon;

        /// <summary>
        /// Password para logarse contra ADAM
        /// </summary>
        private string _PasswordToLogon;
        /// <summary>
        /// Nombre de aplicación
        /// </summary>
        private string _AppName;
        /// <summary>
        /// Segundo de refresco de caché
        /// </summary>
        private int _CacheRefreshInterval;
        /// <summary>
        /// Cadena de conexión con el ADAM
        /// </summary>
        private string _ConnectionString;
        private bool _InitAppDone;
        private DateTime _LastUpdateCacheDate;
        private bool _NewAuthInterface;
        private object _ObjAzApplication;
        private object _ObjAzAuthorizationStoreClass;
        private object _ObjAzScope;
        private string _ScopeName;

        /// <summary>
        /// Inicializa el Provider
        /// </summary>
        /// <param name="name"Nombre de la instancia></param>
        /// <param name="config">Parámetros de configuración del Provider</param>
        public override void Initialize(string name, NameValueCollection config)
        {
            if (string.IsNullOrEmpty(name))
            {
                name = "AuthorizationStoreRoleProvider";
            }
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }
            if (string.IsNullOrEmpty(config["description"]))
            {
                config.Add("description", "RoleAuthStoreProvider_description");
            }
            
            this._CacheRefreshInterval = 60;
            this._ScopeName = config["scopeName"];
            
            if (string.IsNullOrEmpty(config["UserNameToLogon"]))
            {
                throw new ArgumentNullException("UserNameToLogon");
            }
            this._UserNameToLogon = config["UserNameToLogon"];
            config.Remove("UserNameToLogon");

            if (string.IsNullOrEmpty(config["PasswordToLogon"]))
            {
                throw new ArgumentNullException("PasswordToLogon");
            }
            this._PasswordToLogon = config["PasswordToLogon"];
            config.Remove("PasswordToLogon");

            if ((this._ScopeName != null) && (this._ScopeName.Length == 0))
            {
                this._ScopeName = null;
            }
            this._ConnectionString = config["connectionStringName"];
            if ((this._ConnectionString == null) || (this._ConnectionString.Length < 1))
            {
                throw new Exception("Connection_name_not_specified");
            }

            if (ConfigurationManager.ConnectionStrings[config["connectionStringName"]] == null)
                throw new ArgumentException("Initialize: connectionStringName incorrecto");

            if (string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[config["connectionStringName"]].ConnectionString))
            {
                throw new ArgumentException("Connection_string_not_found");
            }
            this._ConnectionString = ConfigurationManager.ConnectionStrings[config["connectionStringName"]].ConnectionString;
            this._AppName = config["applicationName"];
            if (string.IsNullOrEmpty(this._AppName))
            {
                this._AppName = "AzMan";
            }
            if (this._AppName.Length > 0x100)
            {
                throw new ArgumentException("Provider_application_name_too_long");
            }

            // Inicializamos la caché
            this._CacheRoles = new Dictionary<string, string[]>();
            _AzMan = new AzManMgr(this._ConnectionString, this._AppName, this._UserNameToLogon, this._PasswordToLogon, "userPrincipalName");

            base.Initialize(name, config);
        }


        /// <summary>
        /// Valida que la caché no haya caducado, y la inicializa si es necesario
        /// </summary>
        internal void ASSERTCache()
        {
            if(this._LastUpdateCacheDate.AddSeconds(this._CacheRefreshInterval) < DateTime.Now)
                lock(this)
                {
                    if(this._LastUpdateCacheDate.AddSeconds(this._CacheRefreshInterval) < DateTime.Now)
                    {
                        _CacheRoles = new Dictionary<string, string[]>();
                        this._LastUpdateCacheDate = DateTime.Now;
                    }
                }
        }

        /// <summary>
        /// Comprueba si el usuario tiene el rol indicado
        /// </summary>
        /// <param name="username">Nombre de usuario</param>
        /// <param name="roleName">Rol a comprobar</param>
        /// <returns></returns>
        public override bool IsUserInRole(string username, string roleName)
        {
            ASSERTCache();
            Dictionary<string, string[]> instanciaCache = this._CacheRoles;

            if (!instanciaCache.ContainsKey(username))
                instanciaCache[username] = _AzMan.GetRolesForUser(username);

            bool res = false;
            foreach (string rol in instanciaCache[username])
            {
                res = rol.Equals(roleName);
                if (res) break;
            }

            return res;
        }

        /// <summary>
        /// Devuelve un array de nombres de roles que tiene asignados un usuario
        /// </summary>
        /// <param name="username"></param>
        /// <returns></returns>
        public override string[] GetRolesForUser(string username)
        {
            ASSERTCache();
            Dictionary<string, string[]> instanciaCache = this._CacheRoles;

            if (!instanciaCache.ContainsKey(username))
                instanciaCache[username] = _AzMan.GetRolesForUser(username);

            return instanciaCache[username];            
        }


    }
}
